package com.garbagecode.routetable.path;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.garbagecode.routetable.Route;

public class PathImpl implements Path {
  private char[] array = new char[0];
  private Map<Character, Path> nextPathMap = new HashMap<Character, Path>(); // 这个路径的下一个路径
  private List<Route> routeList = new ArrayList<Route>();

  public PathImpl() {

  }

  public PathImpl(char[] array) {
    this.array = array;
  }

  public PathImpl(char[] array, Map<Character, Path> nextPathMap, List<Route> routeList) {
    this.array = array;
    this.nextPathMap = nextPathMap;
    this.routeList = routeList;
  }

  public char[] getArray() {
    return array;
  }

  public Map<Character, Path> getNextPathMap() {
    return nextPathMap;
  }

  public List<Route> getRouteList() {
    return routeList;
  }
  
  public void add(char[] array, int offset, Route route) {
    if (array == null) {
      throw new IllegalArgumentException("array null");
    }

    if (offset >= array.length) {
      throw new IllegalArgumentException("offset out of bounds");
    }

    if (route == null) {
      throw new IllegalArgumentException("route null");
    }

    if (this.array == null) {
      throw new RuntimeException("property 'array' is null");
    }

    char[] array1 = this.array;
    char[] array2 = array;
    int index1 = 0;
    int index2 = offset;

    // 找出两个数组哪里开始不相同
    while (index1 < array1.length && index2 < array2.length) {
      if (array1[index1] != array2[index2]) {
        break;
      }

      index1++;
      index2++;
    }

    // 数组 array1 跟 array2 不相同的部分(从开始不相同的那个位置算)作为下一个路径
    if (index1 < array1.length) {
      PathImpl newPath = createPath();
      newPath.array = Arrays.copyOfRange(array1, index1, array1.length);
      newPath.nextPathMap = nextPathMap;
      newPath.routeList = routeList;

      this.array = Arrays.copyOf(array1, index1);
      this.nextPathMap = new HashMap<Character, Path>();
      this.routeList = new ArrayList<Route>();

      //
      this.nextPathMap.put(newPath.array[0], newPath);
    }

    if (index2 >= array2.length) {
      this.routeList.add(route);
      return;
    }

    Path nextPath = this.nextPathMap.get(array2[index2]);

    // 数组 array2 跟 array1 不相同的部分(从开始不相同的那个位置算)作为下一个路径
    if (nextPath == null) {
      PathImpl newPath = createPath();
      newPath.array = Arrays.copyOfRange(array2, index2, array2.length);
      newPath.routeList.add(route);

      //
      this.nextPathMap.put(newPath.array[0], newPath);
    } else {
      nextPath.add(array2, index2, route);
    }
  }

  public Route get(char[] array, int offset) {
    if (array == null) {
      throw new IllegalArgumentException("array null");
    }

    if (offset >= array.length) {
      throw new IllegalArgumentException("offset out of bounds");
    }

    char[] array1 = this.array;
    char[] array2 = array;
    int index1 = 0;
    int index2 = offset;

    // 找出两个数组哪里开始不相同
    while (index1 < array1.length && index2 < array2.length) {
      if (array1[index1] != array2[index2]) {
        break;
      }

      index1++;
      index2++;
    }

    if (index1 < array1.length) {
      return null;
    }

    Route route = null;
    
    // 从当前路径里获取路由
    if (this.routeList.size() > 0) {
      route = getRoute(routeList, array2, index2);
    }

    // 从下一个路径里获取路由
    if (route == null && index2 < array2.length) {
      Path nextPath = this.nextPathMap.get(array2[index2]);
      route = nextPath != null ? nextPath.get(array2, index2) : null;
    }

    return route;
  }
  
  /**
   * 返回根据参数  array 和 offset 在 routeList 中匹配到的第一个路由
   * @param routeList
   * @param array
   * @param offset
   * @return
   */
  protected Route getRoute(List<Route> routeList, char[] array, int offset) {
    if (routeList == null) {
      throw new IllegalArgumentException("routeList null");
    }

    if (array == null) {
      throw new IllegalArgumentException("array null");
    }

    if (offset > array.length) {
      return null;
    }

    String[] strArray = null;

    // "a/b/c" => ["a", "b", "c"]
    // ""      => [""]
    if (offset < array.length) {
      strArray = new String(array, offset, array.length - offset).split("/", -1);
    } else {
      strArray = new String[] { "" };
    }

    for (Route route : routeList) {
      if (route.getFirstPathParameterIndex() < 0) { // 没有路径参数
        if (offset >= array.length) {
          return route;
        }
      } else if (match(route.getSegmentList(), strArray)) { // 有路径参数
        return route;
      }
    }

    return null;
  }

  /**
   * 判断参数  strArray 是否跟 segmentList 匹配
   * @param segmentList
   * @param strArray
   * @return
   */
  protected boolean match(List<Segment> segmentList, String[] strArray) {
    if (segmentList == null) {
      throw new IllegalArgumentException("segmentList null");
    }

    if (strArray == null) {
      throw new IllegalArgumentException("strArray null");
    }

    if (segmentList.size() != strArray.length) {
      return false;
    }

    Segment segment = null;

    // "/zoos/:zooId/animals/:animalId"
    // 匹配 ["zoos", "gancheng", "animals", "fish"]
    //
    // "/zoos/:zooId/animals/:animalId"
    // 不匹配 ["zoo", "gancheng", "animals", "fish"]
    for (int i = 0; i < strArray.length; i++) {
      segment = segmentList.get(i);

      if (segment.getType() == Segment.TYPE_STATIC && !segment.getName().equals(strArray[i])) {
        return false;
      }
    }

    return true;
  }
  
  /**
   * 创建并返回一个 Path 实例
   * @return
   */
  protected PathImpl createPath() {
    return new PathImpl();
  }

}
